<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Helpers\Auth;
use App\Helpers\URL;
use App\Services\ContactService;
use InvalidArgumentException;
use Riverside\Express\Application;
use Riverside\Express\Request;
use Riverside\Express\Response;

class ContactController
{
    /**
     * Obtiene los campos necesarios para registrar un contacto.
     */
    private function getContactFields(): array
    {
        return ['fullname', 'phone', 'email', 'company', 'photo', 'website', 'birthday', 'notes'];
    }

    /**
     * Obtiene una instancia del servicio de contactos.
     */
    private function contactService(Application $app): ContactService
    {
        return new ContactService($app);
    }

    /**
     * Muestra la vista de todos los contactos registrados con paginación.
     */
    public function index(Request $req, Response $res): void
    {
        $success = $req->session['success'] ?? null;
        $error = $req->session['error'] ?? null;
        $event = $req->query[URL::getQueryEventName()] ?? null;

        // Obtiene el id del usuario autenticado.
        $userAuthId = $req->app->local(Auth::getVarname())['id'];

        // Obtiene todos los contactos del usuario con paginación.
        $contacts = $this->contactService($req->app)->getAllUserContactsWithPagination($userAuthId);

        foreach (['success', 'error'] as $key) {
            unset($req->session[$key]);
        }

        $res->render('contacts/index', [
            'app' => $req->app,
            'contacts' => $contacts,
            'success' => $success,
            'error' => $error,
            'event' => $event,
        ]);
    }

    /**
     * Muestra la vista de registro de contactos.
     */
    public function new(Request $req, Response $res): void
    {
        $validations = $values = $status = [];
        $error = $req->session['error'] ?? null;
        $event = $req->query[URL::getQueryEventName()] ?? null;

        // Obtiene los errores de validación y los valores de los campos.
        foreach ($this->getContactFields() as $field) {
            $validations[$field] = $req->session['validations'][$field] ?? null;
            $values[$field] = $req->session['values'][$field] ?? null;
            $status[$field] = isset($validations[$field]) ? 'error' : null;
        }

        foreach (['validations', 'values', 'error'] as $key) {
            unset($req->session[$key]);
        }

        $res->render('contacts/new', [
            'app' => $req->app,
            'validations' => $validations,
            'values' => $values,
            'status' => $status,
            'error' => $error,
            'event' => $event,
        ]);
    }

    /**
     * Registra un nuevo contacto.
     */
    public function create(Request $req, Response $res): void
    {
        $data = [];

        // Obtiene solo los campos necesarios de la petición.
        foreach ($this->getContactFields() as $field) {
            $data[$field] = $req->body[$field] ?? $req->files[$field] ?? null;
        }

        // Obtiene el id del usuario autenticado.
        $userAuthId = $req->app->local(Auth::getVarname())['id'];

        try {
            // Registra la información del contacto.
            $this->contactService($req->app)->registerUserContact($userAuthId, $data);
        } catch (InvalidArgumentException $e) {
            $gump = $req->app->local('gump');

            $req->session['validations'] = $gump->get_errors_array();
            $req->session['values'] = $data;
            $req->session['error'] = $e->getMessage();

            $res->redirect(URL::event('contacts/new', 'contactCreate'));
        }

        $req->session['success'] = 'El contacto se ha agregado correctamente';

        $res->render('components/redirect', ['url' => URL::site('contacts')]);
    }

    /**
     * Muestra la vista de un contacto.
     */
    public function show(Request $req, Response $res): void
    {
        $contactId = $req->params['uuid'];

        // Obtiene el id del usuario autenticado.
        $userAuthId = $req->app->local(Auth::getVarname())['id'];

        try {
            // Obtiene la información del contacto del usuario.
            $contact = $this->contactService($req->app)->getUserContactDetails($contactId, $userAuthId);
        } catch (InvalidArgumentException $e) {
            $req->session['error'] = $e->getMessage();

            $res->redirect(URL::event('contacts', 'contactsList', 'contactsList'));
        }

        $event = $req->query[URL::getQueryEventName()] ?? null;

        $res->render('contacts/show', [
            'app' => $req->app,
            'contact' => $contact,
            'event' => $event,
        ]);
    }

    /**
     * Muestra la vista para modificar un contacto.
     */
    public function edit(Request $req, Response $res): void
    {
        $contactId = $req->params['uuid'];

        // Obtiene el id del usuario autenticado.
        $userAuthId = $req->app->local(Auth::getVarname())['id'];

        // Obtiene la información del contacto del usuario.
        try {
            $contact = $this->contactService($req->app)->getUserContactDetails($contactId, $userAuthId);
        } catch (InvalidArgumentException $e) {
            $req->session['error'] = $e->getMessage();

            $res->redirect(URL::event('contacts', 'contactsList', 'contactsList'));
        }

        $validations = $values = $status = [];
        $error = $req->session['error'] ?? null;
        $event = $req->query[URL::getQueryEventName()] ?? null;

        // Obtiene los errores de validación y los valores de los campos.
        foreach ($this->getContactFields() as $field) {
            $validations[$field] = $req->session['validations'][$field] ?? null;
            $values[$field] = $req->session['values'][$field] ?? $contact[$field] ?? null;
            $status[$field] = isset($validations[$field]) ? 'error' : null;
        }

        foreach (['validations', 'values', 'error'] as $key) {
            unset($req->session[$key]);
        }

        $res->render('contacts/edit', [
            'app' => $req->app,
            'contactId' => $contact['id'],
            'validations' => $validations,
            'values' => $values,
            'status' => $status,
            'error' => $error,
            'event' => $event,
        ]);
    }

    /**
     * Modifica un contacto.
     */
    public function update(Request $req, Response $res): void
    {
        $data = [];

        // Obtiene solo los campos necesarios de la petición.
        foreach ($this->getContactFields() as $field) {
            $data[$field] = $req->body[$field] ?? null;
        }

        $contactId = $req->params['uuid'];

        // Obtiene el id del usuario autenticado.
        $userAuthId = $req->app->local(Auth::getVarname())['id'];

        try {
            // Modifica la información del contacto del usuario.
            $this->contactService($req->app)->updateUserContact($contactId, $userAuthId, $data);
        } catch (InvalidArgumentException $e) {
            $gump = $req->app->local('gump');

            $req->session['validations'] = $gump->get_errors_array();
            $req->session['values'] = $data;
            $req->session['error'] = $e->getMessage();

            $res->redirect(URL::event(['contacts', 'edit', $contactId], "contactUpdate_{$contactId}"));
        }

        $req->session['success'] = 'El contacto se ha modificado correctamente';

        $res->redirect(URL::event('contacts', 'contactsList', 'contactsList'));
    }

    /**
     * Muestra la vista para eliminar un contacto.
     */
    public function remove(Request $req, Response $res): void
    {
        $contactId = $req->params['uuid'];
        $error = $req->session['error'] ?? null;
        $event = $req->query[URL::getQueryEventName()] ?? null;

        unset($req->session['error']);

        $res->render('contacts/remove', [
            'app' => $req->app,
            'contactId' => $contactId,
            'error' => $error,
            'event' => $event,
        ]);
    }

    /**
     * Elimina un contacto.
     */
    public function delete(Request $req, Response $res): void
    {
        $contactId = $req->params['uuid'];

        // Obtiene el id del usuario autenticado.
        $userAuthId = $req->app->local(Auth::getVarname())['id'];

        try {
            // Elimina la información del contacto del usuario.
            $this->contactService($req->app)->deleteUserContact($contactId, $userAuthId);
        } catch (InvalidArgumentException $e) {
            $req->session['error'] = $e->getMessage();

            $res->redirect(URL::event(['contacts', 'remove', $contactId], "contactDelete_{$contactId}"));
        }

        $req->session['success'] = 'El contacto se ha eliminado correctamente';

        $res->redirect(URL::event('contacts', 'contactsList', 'contactsList'));
    }
}
